package common

import (
	"context"
	"encoding/json"
	"github.com/gorhill/cronexpr"
	"strings"
	"time"
)

// 定时任务
type WorkerJob struct {
	Name       string `json:"name"`     // 任务名字
	PlanRunNum int    `json:"runNum"`   // 计划任务执行次数
	Command    string `json:"command"`  // 任务内容
	CronExpr   string `json:"cronExpr"` // cron表达式
	OutTime    int    `json:"outTime"`  // 任务超时时间
	JobType    int    `json:"jobType"`  // 任务类型
	JobBatch   string `json:"jobBatch"` // 任务批次（同一个cookie的批次号是一样的）
}

// 任务执行状态
type WorkerJobExecuteInfo struct {
	WorkerJob       *WorkerJob         // 任务信息
	PlanTime        time.Time          // 理论上调度时间
	RealTime        time.Time          // 实际调度时间
	BatchRunNum     int                // 第几次执行该任务
	PlanRunTotalNum int                // 计划执行次数
	JobBatch        string             // 任务批次（同一个cookie的批次号是一样的）
	CancelCtx       context.Context    // 用于取消command的context
	CancelFunc      context.CancelFunc // 用于取消command执行的cancel函数
}

// 任务执行结果
type WorkerJobExecuteResult struct {
	WorkerJobSchedulerPlan *WorkerJobSchedulerPlan // 任务调度计划
	WorkerJobExecuteInfo   *WorkerJobExecuteInfo   // 任务执行状态
	JobBatch               string                  // 任务批次号
	Output                 []byte                  // 输出结果
	Err                    error                   // 错误原因
	StartTime              time.Time               // 启动时间
	EndTime                time.Time               // 结束时间
}

// HTTP接口应答
type Response struct {
	Error int         `josn:"errno"`
	Msg   string      `json:"msg"`
	Data  interface{} `json:"data"`
}

// 变化事件
type WorkerJobEvent struct {
	EventType int // SAVE DELETE
	WorkerJob *WorkerJob
}

// 任务调度计划
type WorkerJobSchedulerPlan struct {
	WorkerJobs      map[string]*WorkerJob // 任务信息
	WorkerJob       *WorkerJob            // 实际执行的任务列表
	Expr            *cronexpr.Expression  // 解析好的cronexpr表达式
	NexTIme         time.Time             // 下次执行时间
	RealRunTotalNum int                   // 实际执行总次数
	PlanRunToTalNum int                   // 计划执行总次数
	JobBatch        string                // 任务批次号
}

// 应答方法
func BuildResponse(errno int, msg string, data interface{}) (resp []byte, err error) {
	// 1、定义一个response
	var (
		response Response
	)

	response.Error = errno
	response.Msg = msg
	response.Data = data

	// 2、序列化json
	resp, err = json.Marshal(response)

	return
}

// 反序列化WorkerJob
func UnpackWorkerJob(value []byte) (ret *WorkerJob, err error) {
	var (
		workerJob *WorkerJob
	)

	workerJob = &WorkerJob{}

	if err = json.Unmarshal(value, workerJob); err != nil {
		return
	}
	ret = workerJob

	return
}

// 任务变化事件，有两种 1、更新（包含创建 PUT）任务 2、（DELETE）删除任务
func BuildWorkerJobEvent(eventType int, workerJob *WorkerJob) (workerJobEvent *WorkerJobEvent) {
	return &WorkerJobEvent{
		EventType: eventType,
		WorkerJob: workerJob,
	}
}

// 从etcd的key中提取任务名
// 从/worker/cron/jobs/job10 提取 job10
func ExtractWorkerJobName(jobKey string) string {
	return strings.TrimPrefix(jobKey, WORKER_JOB_SAVE_DIR)
}

// 构造执行计划
func BuildWorkerJobSchedulerPlan(workerJob *WorkerJob) (workerJobSchedulerPlan *WorkerJobSchedulerPlan, err error) {
	var (
		expr *cronexpr.Expression
	)

	// 解析workerJob的cron表达式
	if expr, err = cronexpr.Parse(workerJob.CronExpr); err != nil {
		return
	}

	// 生成任务调度计划对象
	workerJobSchedulerPlan = &WorkerJobSchedulerPlan{
		WorkerJobs:      make(map[string]*WorkerJob, 0),
		WorkerJob:       workerJob, // 执行第一次提交的任务内容
		Expr:            expr,
		NexTIme:         expr.Next(time.Now()),
		RealRunTotalNum: 0,
		PlanRunToTalNum: workerJob.PlanRunNum,
		JobBatch:        workerJob.JobBatch,
	}

	return
}

// 构建执行状态信息
func BuildWorkerJobExecuteInfo(workerJobPlan *WorkerJobSchedulerPlan) (workerJobExecuteInfo *WorkerJobExecuteInfo) {

	workerJobExecuteInfo = &WorkerJobExecuteInfo{
		WorkerJob:       workerJobPlan.WorkerJob,
		PlanTime:        workerJobPlan.NexTIme,
		JobBatch:        workerJobPlan.JobBatch,
		RealTime:        time.Now(),
		BatchRunNum:     workerJobPlan.RealRunTotalNum,
		PlanRunTotalNum: workerJobPlan.PlanRunToTalNum,
	}

	workerJobExecuteInfo.CancelCtx, workerJobExecuteInfo.CancelFunc = context.WithCancel(context.TODO())

	return
}
